package com.whc.message.ws;

import com.alibaba.fastjson.JSONObject;

import com.whc.common.constant.JwtClaimsConstant;
import com.whc.common.constant.MessageConstant;
import com.whc.common.constant.RedisHeader;
import com.whc.common.context.BaseContext;

import com.whc.common.utils.JwtUtil;
import com.whc.message.domain.dto.MessageDto;
import com.whc.message.domain.entity.Message;
import com.whc.message.service.MessageService;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;


import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;



/**
     * @ServerEndpoint 该注解可以将类定义成一个WebSocket服务器端，
 * @OnOpen 表示有浏览器链接过来的时候被调用
 * @OnClose 表示浏览器发出关闭请求的时候被调用
 * @OnMessage 表示浏览器发消息的时候被调用
 * @OnError 表示报错了
 */

@Component
@Slf4j
@ServerEndpoint(value = "/ws/{token}")
public class ChatEndpoint {


    private static MessageService messageService;
    private static RedisTemplate<String,Object> redisTemplate;

    @Autowired
    public void setMessageService(MessageService messageService) {
        ChatEndpoint.messageService = messageService;
    }
    @Resource
    public void setRedisTemplate(RedisTemplate<String,Object> redisTemplate){ChatEndpoint.redisTemplate=redisTemplate;}

    //ConcurrentHashMap是一个线程安全的HashMap
    //这个map给上static保证是单例的，final保证不要被重新赋初值
    private static final Map<Long, Session> onlineUser=new ConcurrentHashMap<>();


    @OnOpen
    public void OnOpen(Session session,@PathParam("token") String token){
        //通过token来获取当前用户信息
        System.out.println("token:"+token);
//        String token = getQueryParam(queryString, "token");
        long userId=-1;
        try{
            log.info("校验令牌");
            Claims claims = JwtUtil.parseJWT("tryproject", token);
            userId = (int)claims.get(JwtClaimsConstant.USER_ID);

            log.info("onoPen中获取到当前用户的id:{}",userId);
            BaseContext.setCurrentId(userId);

        }catch (Exception e){
            log.info("令牌校验失败");

        }

//        System.out.println("onOpen中的"+userId);
        //存放session，并用用户id去对应他
        onlineUser.put(userId,session);

        log.info("用户{}上线了",userId);
        log.info("当前所有用户{}",onlineUser);
        //后端修改状态，确保数据准确
//        if(!redisTemplate.hasKey(RedisHeader.userOnline+userId)){
//            //将当前时间格式化为字符串，存入redis
//            redisTemplate.opsForValue().set(RedisHeader.userOnline+userId,DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
//        }
        setUserStatus(String.valueOf(userId),"存活",40,TimeUnit.SECONDS);
        //通知其他用户当前用户上线了
        Message message = new Message();
        message.setSenderId(userId);
        message.setAccepterId(0);
        String mess = JSONObject.toJSONString(message);
        broadcastAllUsers(mess);
    }

    private void broadcastAllUsers(String message){
        Set<Long> keySet = onlineUser.keySet();
        for(Long keyone:keySet){
            Session session = onlineUser.get(keyone);
            //排除自己
            long userId= BaseContext.getCurrentId();
            if(keyone==userId)continue;
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //指定发送给谁
    @OnMessage
    public void OnMessage(String msg) {
       try{
           //前端发过来的消息包含，发送方id，接收方id，正文
           //发送消息
           System.out.println(msg);
           MessageDto messageDto = JSONObject.parseObject(msg, MessageDto.class);
           messageDto.setCreateTime(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
           long accepterId = messageDto.getAccepterId();
           Session session = onlineUser.get(accepterId);
           Session session2 = onlineUser.get(messageDto.getSenderId());
           //如果发送端和接收端的id相同，表明这是一次心跳请求，更新它的生存时间
           if(accepterId==messageDto.getSenderId()){
               renewUserStatus(String.valueOf(accepterId),30,TimeUnit.SECONDS);
           }
           //接收端
           if(session!=null){
               session.getBasicRemote().sendText(JSONObject.toJSONString(messageDto));
           }
           //发送端
           if(session2!=null){
               session2.getBasicRemote().sendText(JSONObject.toJSONString(messageDto));
           }
           if(accepterId!=messageDto.getSenderId()){
               //消息存入数据库
               Message message=new Message();
               BeanUtils.copyProperties(messageDto,message);
               message.setCreateTime(LocalDateTime.now());
               message.setType(MessageConstant.Message_Unread);
               messageService.add(message);
           }


       }catch (Exception e){
           e.printStackTrace();
       }

    }

    @OnClose
    public void OnClose(Session session){
        //通过token来获取当前用户信息
        String queryString = session.getRequestURI().getQuery();
        String token = getQueryParam(queryString, "token");
        log.info(token);
        String userid="-1";
        try{
            log.info("校验令牌");
            Claims claims = JwtUtil.parseJWT("tryproject", token);
            userid = claims.get(JwtClaimsConstant.USER_ID).toString();
            log.info("onoPen中获取到当前用户的id:{}",userid);
            BaseContext.setCurrentId(Long.parseLong(userid));

        }catch (Exception e){
            log.info("令牌校验失败");

        }
        //通知其他用户当前用户下线了
//        long userId= BaseContext.getCurrentId();
//        System.out.println("onClose中的"+userId);
        Message message = new Message();
        message.setSenderId(Long.parseLong(userid));
        message.setAccepterId(-1);
        String mess = JSONObject.toJSONString(message);
        broadcastAllUsers(mess);
        //删除用户在redis中的在线状态
        if(redisTemplate.hasKey(RedisHeader.userOnline+userid)){
            redisTemplate.delete(RedisHeader.userOnline+userid);
        }
        //从onlineUser中剔除当前用户
        onlineUser.remove(userid);
        log.info("用户{}下线了",userid);
        log.info("当前所有用户{}",onlineUser);
    }

    // 辅助方法，获取查询参数值
    private String getQueryParam(String queryString, String param) {
        if (queryString != null) {
            String[] pairs = queryString.split("&");
            for (String pair : pairs) {
                String[] keyValue = pair.split("=");
                if (keyValue[0].equals(param)) {
                    return keyValue[1];
                }
            }
        }
        return null;
    }

    // 设置用户标识，设置过期时间
    public void setUserStatus(String userId, String status, long expirationTime, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(RedisHeader.userOnline+userId, status, expirationTime, timeUnit);
    }

    // 查询用户标识是否存在
    public boolean isUserActive(String userId) {
        return redisTemplate.hasKey(RedisHeader.userOnline+userId);  // 如果返回true，说明标识存在，用户活跃
    }

    // 续期操作
    public void renewUserStatus(String userId, long additionalTime, TimeUnit timeUnit) {
        if (isUserActive(userId)) {
            redisTemplate.expire(RedisHeader.userOnline+userId, additionalTime, timeUnit);  // 续期
        }
    }
}
